package com.nowoe.data.exector;

import com.nowoe.data.config.BoundSql;
import com.nowoe.data.config.Configuration;
import com.nowoe.data.statement.MapperStatement;
import com.nowoe.data.utils.GenericTokenParser;
import com.nowoe.data.utils.ParameterMapping;
import com.nowoe.data.utils.ParameterMappingTokenHandler;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 倚栏听风 2023/9/14 16:46
 */
public class SimpleExecutor implements Executor {
    private Connection connection = null;
    private PreparedStatement preparedStatement = null;
    private ResultSet resultSet = null;

    @Override
    public <E> List<E> query(Configuration configuration, MapperStatement mapperStatement, Object param) throws Exception {
        //加载驱动获取数据库连接
        connection = configuration.getDataSource().getConnection();
        String sql = mapperStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);
        String parsedSql = boundSql.getParsedSql();
        preparedStatement = connection.prepareStatement(parsedSql);
        if (param != null) {
            //占位符设置参数
            String parameterType = mapperStatement.getParameterType();
            Class<?> parameterTypeClass = Class.forName(parameterType);
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
            for (int i = 0; i < parameterMappings.size(); i++) {
                String content = parameterMappings.get(i).getContent();
                Field field = parameterTypeClass.getDeclaredField(content);
                field.setAccessible(true);
                Object o = field.get(param);
                preparedStatement.setObject(i + 1, o);
            }
        }
        //执行
        resultSet = preparedStatement.executeQuery();
        //处理返回
        String resultType = mapperStatement.getResultType();
        Class<?> resultTypeClass = Class.forName(resultType);
        List<E> list = new ArrayList<>();
        while (resultSet.next()) {
            ResultSetMetaData metaData = resultSet.getMetaData();
            Object instance = resultTypeClass.getDeclaredConstructor().newInstance();
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                //字段名
                String columnName = metaData.getColumnName(i);
                //字段值
                Object value = resultSet.getObject(columnName);
                //获得属性描述器
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
                Method writeMethod = propertyDescriptor.getWriteMethod();
                writeMethod.invoke(instance, value);
            }
            list.add((E) instance);
        }

        return list;
    }

    @Override
    public int update(Configuration configuration, MapperStatement mapperStatement, Object param) throws Exception {
        execute(configuration, mapperStatement, param);
        //执行
        return preparedStatement.executeUpdate();

    }

    @Override
    public int delete(Configuration configuration, MapperStatement mapperStatement, Object param) throws Exception {
        execute(configuration, mapperStatement, param);
        //执行
        return preparedStatement.executeUpdate();
    }

    @Override
    public int insert(Configuration configuration, MapperStatement mapperStatement, Object param) throws Exception {
        execute(configuration, mapperStatement, param);
        return preparedStatement.executeUpdate();
    }

    private void execute(Configuration configuration, MapperStatement mapperStatement, Object param) throws SQLException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        //加载驱动获取数据库连接
        connection = configuration.getDataSource().getConnection();
        String sql = mapperStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);
        String parsedSql = boundSql.getParsedSql();
        preparedStatement = connection.prepareStatement(parsedSql);
        if (param != null) {
            //占位符设置参数
            String parameterType = mapperStatement.getParameterType();
            Class<?> parameterTypeClass = Class.forName(parameterType);
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
            for (int i = 0; i < parameterMappings.size(); i++) {
                if (parameterTypeClass.getName().startsWith("java.lang")) {
                    preparedStatement.setObject(i + 1, param);
                } else {
                    String content = parameterMappings.get(i).getContent();
                    Field field = parameterTypeClass.getDeclaredField(content);
                    field.setAccessible(true);
                    Object o = field.get(param);
                    preparedStatement.setObject(i + 1, o);
                }
            }
        }
    }

    /**
     * 将sql中#{value} 替换成？并保存值
     *
     * @param sql
     * @return
     */
    private BoundSql getBoundSql(String sql) {
        ParameterMappingTokenHandler tokenHandler = new ParameterMappingTokenHandler();
        GenericTokenParser tokenParser = new GenericTokenParser("#{", "}", tokenHandler);
        //把#替换成?的sql
        String parsedSql = tokenParser.parse(sql);
        //#{id} #{username}中的 id 、username
        List<ParameterMapping> parameterMappings = tokenHandler.getParameterMappings();
        return new BoundSql(parsedSql, parameterMappings);
    }

    /**
     * 关闭连接
     */
    @Override
    public void close() {

        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        if (preparedStatement != null) {
            try {
                preparedStatement.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }


}
